package com.brotherly.util;

import java.io.PrintWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class JSonSax
{
    protected static final int none = 0;
    protected static final int array = 1;
    protected static final int map = 2;

    protected class Status
    {
        public Status(int type)
        {
            this.type = type;
            this.index = 0;
        }

        int type;
        int index;
    }

    protected int indent = 0;
    protected PrintWriter writer;
    protected LinkedList<Status> stack;

    protected Status current;

    public JSonSax(Writer writer)
    {
        if (writer instanceof PrintWriter)
        {
            this.writer = (PrintWriter) writer;
        } else
        {
            this.writer = new PrintWriter(writer);
        }

        stack = new LinkedList<Status>();
        current = new Status(none);
    }

    protected PrintWriter writeIndent()
    {
        for (int index = 0; index != indent; ++index)
            writer.write("    ");

        return writer;
    }

    protected void increaseIndent()
    {
        ++indent;
    }

    protected void decreaseIndent()
    {
        --indent;
    }

    protected void pushStatus(int type)
    {
        stack.addFirst(current);
        current = new Status(type);

        increaseIndent();
    }

    protected void popStatus()
    {
        current = stack.removeFirst();

        decreaseIndent();
    }

    protected void newElement(boolean needln)
    {
        if (current.index != 0)
        {
            if (current.type == array)
            {
                writer.print(", ");
            } else if (current.type == map)
            {
                if (current.index % 2 == 1)
                {
                    writer.print(": ");
                } else
                {
                    writer.println(",");
                    writeIndent();
                }
            }

            if (needln)
                writer.println();
        } else
        {
            if (!needln)
                writeIndent();
        }

        ++current.index;
    }

    public void flush()
    {
        writer.flush();
    }

    public void writeBoolean(boolean value)
    {
        newElement(false);

        writer.print(value ? "true" : "false");
    }

    public void writeLong(long value)
    {
        newElement(false);

        writer.print(value);
    }

    public void writeDouble(double value)
    {
        newElement(false);

        writer.print(value);
    }

    public void writeString(String value)
    {
        newElement(false);

        if (value == null)
        {
            writer.print("null");
            return;
        }

        writer.print("\"");
        writer.print(value.replaceAll("\"", "\\\""));
        writer.print("\"");
    }

    public void writeNull()
    {
        newElement(false);

        writer.print("null");
    }

    public void startArray()
    {
        newElement(true);
        writeIndent().println("[");

        pushStatus(array);
    }

    public void endArray()
    {
        popStatus();

        if (current.index != 0)
            writer.println();
        writeIndent().print("]");
    }

    public void startMap()
    {
        newElement(true);
        writeIndent().println("{");

        pushStatus(map);
    }

    public void endMap()
    {
        popStatus();

        if (current.index != 0)
            writer.println();
        writeIndent().print("}");
    }

    public static void writeObject(Object value, Writer writer)
    {
        writeObject(value, new JSonSax(writer));
    }

    @SuppressWarnings(
    {
            "rawtypes"
    })
    public static void writeObject(Object value, JSonSax sax)
    {
        if (value == null)
        {
            sax.writeNull();
            return;
        }

        if (value instanceof String)
        {
            sax.writeString((String) value);
            return;
        }

        if (value instanceof Double)
        {
            if (((Double) value).isInfinite() || ((Double) value).isNaN())
            {
                sax.writeNull();
            } else
            {
                sax.writeDouble(((Double) value).doubleValue());
            }

            return;
        }

        if (value instanceof Float)
        {
            if (((Double) value).isInfinite() || ((Double) value).isNaN())
            {
                sax.writeNull();
            } else
            {
                sax.writeDouble(((Float) value).doubleValue());
            }

            return;
        }

        if (value instanceof Number)
        {
            sax.writeLong(((Number) value).longValue());
            return;
        }

        if (value instanceof Boolean)
        {
            sax.writeBoolean(((Boolean) value).booleanValue());
            return;
        }

        if (value instanceof Map)
        {
            Map map = (Map) value;

            sax.startMap();

            Iterator it = map.entrySet().iterator();
            while (it.hasNext())
            {
                Entry entry = (Entry) it.next();

                writeObject(entry.getKey(), sax);
                writeObject(entry.getValue(), sax);
            }

            sax.endMap();

            return;
        }

        if (value instanceof List)
        {
            List list = (List) value;

            sax.startArray();

            Iterator it = list.iterator();
            while (it.hasNext())
            {
                Object obj = it.next();

                writeObject(obj, sax);
            }

            sax.endArray();

            return;
        }

        sax.writeString(value.toString());
    }
}
